//
// Created by root on 17-3-10.
//

#include <fstream>
#include <cstring>
#include <dirent.h>
#include <ftw.h>
#include <fnmatch.h>
#include <curlcpp/curl_header.h>
#include <curl_easy.h>
#include "sand.h"
#include "mpack.h"
#include "json.h"
#include "path.h"
#include "resolver.h"

//#include "ctrl_spdlog.h"
#include "ctrl_app.h"
#include "ctrl_cfg.h"

#include "CImg.h"


std::vector<std::string> CtrlApp::_imgfile_vec;

CtrlApp::CtrlApp() {

}

CtrlApp::~CtrlApp() {

}

void CtrlApp::set_picture_parm_st(Json::Value &parm_json) {
    Json::Value angle_json = parm_json["rotate"]["angle"];
    Json::Value cond_json = parm_json["rotate"]["cond"];
    Json::Value draw_text_json = parm_json["draw_text"]["text"];
    Json::Value draw_x_json = parm_json["draw_text"]["coordinate"]["x"];
    Json::Value draw_y_json = parm_json["draw_text"]["coordinate"]["y"];
    Json::Value draw_r_json = parm_json["draw_text"]["color"]["r"];
    Json::Value draw_g_json = parm_json["draw_text"]["color"]["g"];
    Json::Value draw_b_json = parm_json["draw_text"]["color"]["b"];
    Json::Value draw_size_json = parm_json["draw_text"]["text_size"];
    Json::Value resize_pdx_json = parm_json["resize"]["pdx"];
    Json::Value resize_pdy_json = parm_json["resize"]["pdy"];
    Json::Value resize_pdz_json = parm_json["resize"]["pdz"];
    Json::Value resize_pdv_json = parm_json["resize"]["pdv"];
    Json::Value resize_type_json = parm_json["resize"]["interp_type"];

    float angle = angle_json.asFloat();
    uint32_t cond = cond_json.asUInt();
    std::string draw_text = draw_text_json.asString();
    int draw_x = draw_x_json.asInt64();
    int draw_y = draw_y_json.asInt64();
    uint32_t draw_r =  draw_r_json.asUInt();
    uint32_t draw_g =  draw_g_json.asUInt();
    uint32_t draw_b =  draw_b_json.asUInt();
    uint32_t draw_size = draw_size_json.asUInt();
    int resize_pdx = resize_pdx_json.asInt64();
    int resize_pdy = resize_pdy_json.asInt64();
    int resize_pdz = resize_pdz_json.asInt64();
    int resize_pdv = resize_pdv_json.asInt64();
    uint32_t resize_type = resize_type_json.asUInt();

    _picture_parm_st.rotate_angle = angle;
    _picture_parm_st.rotate_cond = cond;
    sprintf(_picture_parm_st.draw_text, "%s", draw_text.c_str());
    _picture_parm_st.draw_x_coordinate = draw_x;
    _picture_parm_st.draw_y_coordinate = draw_y;
    _picture_parm_st.draw_color_r = draw_r;
    _picture_parm_st.draw_color_g = draw_g;
    _picture_parm_st.draw_color_b = draw_b;
    _picture_parm_st.draw_text_size_px = draw_size;
    _picture_parm_st.resize_pdx = resize_pdx;
    _picture_parm_st.resize_pdy = resize_pdy;
    _picture_parm_st.resize_pdz = resize_pdz;
    _picture_parm_st.resize_pdv = resize_pdv;
    _picture_parm_st.resize_interp_type = resize_type;

    _picture_parm_st.position.lng = _position_st.lng;
    _picture_parm_st.position.lat =  _position_st.lat;

}

CtrlApp::PICTURE_PARM_ST CtrlApp::get_picture_parm_st() {
    return _picture_parm_st;
}

void CtrlApp::set_position_info_st(POSITION_INFO_ST position_st) {
    _position_st.lng = position_st.lng;
    _position_st.lat = position_st.lat;
}

CtrlApp::POSITION_INFO_ST CtrlApp::get_position_info_st() {
    return _position_st;
}

bool CtrlApp::picture_process(std::string picture_name) {
    float pic_rotate_angle = _picture_parm_st.rotate_angle;
    uint32_t pic_rotate_cond = _picture_parm_st.rotate_cond;
    int pic_draw_x = _picture_parm_st.draw_x_coordinate;
    int pic_draw_y = _picture_parm_st.draw_y_coordinate;
    std::string pic_draw_text;
    if (_picture_parm_st.position.lng > 0.0 && _picture_parm_st.position.lat > 0.0) {
        std::ostringstream lngbuf, latbuf;
        lngbuf << _picture_parm_st.position.lng;
        latbuf << _picture_parm_st.position.lat;
        pic_draw_text = std::string(_picture_parm_st.draw_text) +
                        " Lng: " + lngbuf.str() +
                        " Lat: " + latbuf.str();
    } else {
        pic_draw_text = std::string(_picture_parm_st.draw_text);
    }
    uint32_t pic_draw_color_r = _picture_parm_st.draw_color_r;
    uint32_t pic_draw_color_g = _picture_parm_st.draw_color_g;
    uint32_t pic_draw_color_b = _picture_parm_st.draw_color_b;
    uint32_t pic_draw_text_size = _picture_parm_st.draw_text_size_px;
    int pic_resize_pdx = _picture_parm_st.resize_pdx;
    int pic_resize_pdy = _picture_parm_st.resize_pdy;
    int pic_resize_pdz = _picture_parm_st.resize_pdz;
    int pic_resize_pdv = _picture_parm_st.resize_pdv;
    uint32_t pic_resize_type = _picture_parm_st.resize_interp_type;

    cimg_library::CImg<unsigned char> img;
    img.load(picture_name.c_str());
    img.rotate(pic_rotate_angle, pic_rotate_cond);
    unsigned char color_arr[] = { pic_draw_color_r,
                                  pic_draw_color_g ,
                                  pic_draw_color_b };
    img.draw_text(pic_draw_x,
                  pic_draw_y,
                  pic_draw_text.c_str(),
                  color_arr,
                  0,
                  1,
                  pic_draw_text_size);

    img.resize(pic_resize_pdx,
               pic_resize_pdy,
               pic_resize_pdz,
               pic_resize_pdv,
               pic_resize_type);

    img.save_jpeg(picture_name.c_str());
    
    return true;
}

int CtrlApp::iterator_imgfile_callback(const char *fpath, const struct stat *sb, int typeflag) {
    const char *filters[] = {
            "*.jpg", "*.jpeg", "*.gif", "*.png"
    };

    if (typeflag == FTW_F) {
        int i;
        
        for (i = 0; i < sizeof(filters) / sizeof(filters[0]); i++) {
            if (fnmatch(filters[i], fpath, FNM_CASEFOLD) == 0) {
                SpdLog::console->info("found image: {0}", fpath);

                _imgfile_vec.push_back(std::string(fpath));
                
                break;
            }
        }
    }

    return 0;
}

bool CtrlApp::timer_delete_cache_file() {
    std::string img_path = pctrl_cfg->get_config_st().snapshot_picture_path;
    
    _imgfile_vec.clear();
    ftw(img_path.c_str(), iterator_imgfile_callback, 16);
    
    for(std::string imgfile : _imgfile_vec) {
        filesystem::path img_path(imgfile.c_str());
        bool b_ret = img_path.remove_file();
        if (b_ret)
            SpdLog::console->info("remove file {0} success!", imgfile);
        else
            SpdLog::console->error("remove file {0} failed!", imgfile);
        
    }
    
    return true;
}

bool CtrlApp::upload_picture_by_curl(std::string upload_url, std::string username, std::string password, uint32_t period) {
    std::string img_path = pctrl_cfg->get_config_st().snapshot_picture_path;
    
    _imgfile_vec.clear();
    ftw(img_path.c_str(), iterator_imgfile_callback, 16);
    
    for(std::string imgfile : _imgfile_vec) {
        struct curl_httppost *formpost = NULL;
        struct curl_httppost *lastptr = NULL;
        struct curl_slist *headerlist = NULL;
        static const char buf[] = "Expect:";
        curl_formadd(&formpost,
                        &lastptr,
                        CURLFORM_COPYNAME, "file",
                        CURLFORM_FILE, imgfile.c_str(),
                        CURLFORM_END);
        curl_formadd(&formpost,
                        &lastptr,
                        CURLFORM_COPYNAME, "username",
                        CURLFORM_COPYCONTENTS, username.c_str(),
                        CURLFORM_END);
        curl_formadd(&formpost,
                        &lastptr,
                        CURLFORM_COPYNAME, "password",
                        CURLFORM_COPYCONTENTS, password.c_str(),
                        CURLFORM_END);
        headerlist = curl_slist_append(headerlist, buf);

        curl::curl_easy easy;

        easy.add<CURLOPT_URL>(upload_url.c_str());
        easy.add<CURLOPT_HTTPHEADER>(headerlist);
        easy.add<CURLOPT_HTTPPOST>(formpost);
        easy.add<CURLOPT_VERBOSE>(1L);

        try {
            easy.perform();
        } catch (curl::curl_easy_exception error) {
            //curl::curlcpp_traceback errors = error.get_traceback();
            //error.print_traceback();
            SpdLog::console->error("curl exception, error code {0}", error.get_code());
        }
    }

    return true;
}

bool CtrlApp::upload_picture_by_ftp(std::string upload_url, std::string username, std::string password, uint32_t period) {
    std::string img_path = pctrl_cfg->get_config_st().snapshot_picture_path;
    
    _imgfile_vec.clear();
    ftw(img_path.c_str(), iterator_imgfile_callback, 16);
    
    for(std::string imgfile : _imgfile_vec) {
        FILE *hd_src;
        struct stat file_info;
        curl_off_t fsize;

        if(stat(imgfile.c_str(), &file_info)) {
            continue;
        }
        fsize = (curl_off_t)file_info.st_size;
        hd_src = fopen(imgfile.c_str(), "rb");

        curl::curl_easy easy;

        easy.add<CURLOPT_URL>(upload_url.c_str());
        easy.add<CURLOPT_UPLOAD>(1L);
        easy.add<CURLOPT_USERNAME>(username.c_str());
        easy.add<CURLOPT_PASSWORD>(password.c_str());
        easy.add<CURLOPT_READDATA>(hd_src);
        easy.add<CURLOPT_INFILESIZE_LARGE>((curl_off_t)fsize);
        easy.add<CURLOPT_VERBOSE>(1L);

        try {
            easy.perform();
        } catch (curl::curl_easy_exception error) {
            //curl::curlcpp_traceback errors = error.get_traceback();
            //error.print_traceback();
            SpdLog::console->error("curl exception, error code {0}", error.get_code());
        }
        fclose (hd_src);    
    }

    return true;
}

CtrlMqtt::CtrlMqtt(std::string id) : mosqpp::mosquittopp(id.c_str()) {
    max_inflight_messages_set(4096);
    
    int32_t m_mqtt_protocol = MQTT_PROTOCOL_V311;
    set_protocol(m_mqtt_protocol);

    reconnect_delay_set(3, 30, true);
}

void CtrlMqtt::on_connect(int rc) {
    SpdLog::console->info("on_connect begin, rc: {0}", rc);
    if (rc == MOSQ_ERR_SUCCESS) {
        SpdLog::console->info("mqtt -> connected with server");
        
        snprintf(mqtt_sub[MQTT_SUB_REPORT], MQSIZE,"%s/%s", TopicRoot, "report");
        subscribe(NULL, mqtt_sub[MQTT_SUB_REPORT], 0);
        SpdLog::console->info("mqtt subcribe topic: {0}", mqtt_sub[MQTT_SUB_REPORT]);

        //publish topics
        snprintf(mqtt_pub[MQTT_PUB_CLIENT_CTRL], MQSIZE, "%s/%s", TopicRoot, "ctrlinfo");
        mhikv_publish_topics.push_front(mqtt_pub[MQTT_PUB_CLIENT_CTRL]);
        
        snprintf(mqtt_pub[MQTT_PUB_CLIENT_SBUF], MQSIZE, "%s/%s", TopicRoot, "mpack");
        mhikv_publish_topics.push_front(mqtt_pub[MQTT_PUB_CLIENT_SBUF]);

        //subscribe topics
        snprintf(mqtt_sub[MQTT_SUB_CLIENT_CTRL], MQSIZE, "%s/%s", TopicRoot, "ctrlinfo");
        mhikv_subscribe_topics.push_front(mqtt_sub[MQTT_SUB_CLIENT_CTRL]);
        subscribe(NULL, mqtt_sub[MQTT_SUB_CLIENT_CTRL], 0);
        SpdLog::console->info("mqtt subcribe topic: {0}", mqtt_sub[MQTT_SUB_CLIENT_CTRL]);
        
        snprintf(mqtt_sub[MQTT_SUB_CLIENT_SBUF], MQSIZE, "%s/%s", TopicRoot, "mpack");
        mhikv_subscribe_topics.push_front(mqtt_sub[MQTT_SUB_CLIENT_SBUF]);
        subscribe(NULL, mqtt_sub[MQTT_SUB_CLIENT_SBUF], 0);
        SpdLog::console->info("mqtt subcribe topic: {0}", mqtt_sub[MQTT_SUB_CLIENT_SBUF]);
       
    } else {
        SpdLog::console->info("mqtt -> impossible to connect with server, rc: {0}", rc);
    }
    SpdLog::console->info("on_connect end");

    /*SpdLog::console->info("on_connect begin, rc: {0}", rc);
    if (!rc) {
        // subscribe(NULL,it->second.c_str(),0);
    }
    SpdLog::console->info("on_connect end");*/
}

void CtrlMqtt::on_disconnect(int rc) {
    SpdLog::console->info("mqtt -> disconnection, rc: {0}", rc);
    
    for (auto it = mhikv_subscribe_topics.begin(); it != mhikv_subscribe_topics.end(); ++it) {
        unsubscribe(NULL, (*it).c_str());
        SpdLog::console->info("mqtt -> unsubscribe topics: {0}", *it);
    }
    
    mhikv_publish_topics.clear();
    mhikv_subscribe_topics.clear();
     
    SpdLog::console->info("on_disconnect end ");
    
    /*SpdLog::console->info("on_disconnect begin, rc: {0}", rc);
    if (!rc) {
        int i =0;
        // unsubscribe(NULL,it->second.c_str());
        for (i=0; i<MQTT_SUB_MAX; i++) {
            unsubscribe(NULL,mqtt_sub[i]);
        }
    }
    SpdLog::console->info("on_disconnect end ");*/
}

void CtrlMqtt::on_subscribe(int mid, int qos_count, const int *granted_qos) {
    SpdLog::console->info("on_subscribe mid: {0}, qos_count: {1}", mid, qos_count);
}

void CtrlMqtt::on_unsubscribe(int mid) {
    SpdLog::console->info("on_unsubscribe ");
}

void CtrlMqtt::on_message(const struct mosquitto_message *msg) {
    SpdLog::console->info("Hikvision mqtt on_message event");
    Json::Value v_parse;
    if (msg) {
        if (strcmp(msg->topic, mqtt_sub[MQTT_SUB_REPORT]) == 0) {
            std::string payload = std::string((char *) msg->payload, msg->payloadlen);
            SpdLog::console->info("msg->payload: {0}", payload);
        }
        for (auto siter = mhikv_subscribe_topics.begin(); siter != mhikv_subscribe_topics.end(); ++siter) {
            if ((std::string) msg->topic == *siter) {
                if(strstr((char*)msg->topic, "/ctrlinfo") != NULL) {
                    std::string payload = std::string((char *) msg->payload, msg->payloadlen);
                    SpdLog::console->info("mqtt ctrl payload: {0}", payload);
                    std::string cmd;
                    std::string gps;
                    Json::Value msg_topic;
                    Json::Reader reader;
                    if (reader.parse(payload, msg_topic)) {
                        if (msg_topic.isObject() && msg_topic.isMember("cmd"))
                            cmd = msg_topic.get("cmd", "" ).asString();
                        if (cmd.compare("snapshot") == 0) {
                                     
                            if (msg_topic.isObject() && msg_topic.isMember("channel")) {
                                
                                const Json::Value& channel_arr = msg_topic["channel"];
                                for (Json::ValueConstIterator it = channel_arr.begin(); it != channel_arr.end(); ++it) {
                                    uint32_t channel = (*it).asUInt();
                                    
                                    Json::Value picture_json = msg_topic["value"]["picture_parm"];
                                    if (picture_json.isObject()) {
                                        pctrl_app->set_picture_parm_st(picture_json);
                                        std::string picture_name;                           
                                        int ret = phikv_api->hikv_capture_picture(channel, picture_name);
                                        if (ret == 0 && !picture_name.empty()) {
                                            pctrl_app->picture_process(picture_name);
                                        }
                                    }
                                    upload_jpgsnap_by_mpack();
                                }
                            }                            
                        }
                        if (cmd.compare("gps") == 0) {
                            std::string lng;
                            std::string lat;
                            if (msg_topic.isObject() && msg_topic.isMember("lng"))
                                lng = msg_topic.get("lng", "").asString();
                            if (msg_topic.isObject() && msg_topic.isMember("lat"))
                                lat = msg_topic.get("lat", "").asString();
                            std::istringstream buflng(lng);
                            std::istringstream buflat(lat);
                            float f_lng = 0.0, f_lat = 0.0;
                            buflng >> f_lng;
                            buflat >> f_lat;
                            CtrlApp::POSITION_INFO_ST position;
                            memset(&position, 0x0, sizeof(position));
                            position.lng = f_lng;
                            position.lat = f_lat;
                            pctrl_app->set_position_info_st(position);
                        }
                        if (cmd.compare("upload") == 0) {
                            std::string upload_url;
                            std::string username;
                            std::string password;
                            std::string mode;
                            uint32_t timeperiod;
                            if (msg_topic.isObject() && msg_topic.isMember("upload_url")) {
                                upload_url = msg_topic.get("upload_url", "").asString();
                            }
                            if (msg_topic.isObject() && msg_topic.isMember("username")) {
                                username = msg_topic.get("username", "").asString();
                            }
                            if (msg_topic.isObject() && msg_topic.isMember("password")) {
                                password = msg_topic.get("password", "").asString();
                            }
                            if (msg_topic.isObject() && msg_topic.isMember("mode")) {
                                mode = msg_topic.get("mode", "").asString();
                            }
                            if (msg_topic.isObject() && msg_topic.isMember("timeperiod")) {
                                timeperiod = msg_topic.get("timeperiod", 60).asUInt();
                            }
                            if (mode == TYPE_CURL) {
                                pctrl_app->upload_picture_by_curl(upload_url, username, password, timeperiod);
                            }
                            if (mode == TYPE_FTP) {
                                pctrl_app->upload_picture_by_ftp(upload_url, username, password, timeperiod);
                            }
                        }
                        if (cmd.compare("cfginfo") == 0) {
                            std::string config_info = pctrl_cfg->get_yaml_config_info();
                            int rc = publish(NULL, mqtt_pub[MQTT_PUB_CLIENT_CTRL], std::strlen(config_info.c_str()), config_info.c_str());
                            if (rc == 0) {
                                //
                            }
                        }
                        if (cmd.compare("general") == 0) {
                            std::string sdklog_dir;
                            uint32_t sdklog_level;
                            bool sdklog_autodel;
                            uint32_t applog_level;
                            bool on_timer_delete_file;
                            uint32_t file_cache_interval;
      
                            if (msg_topic.isObject() && msg_topic.isMember("sdklog_dir")) {
                                sdklog_dir = msg_topic.get("sdklog_dir", "./sdkLog").asString();
                                pctrl_cfg->_general_map["sdklog_dir"] = true;
                            }
                            if (msg_topic.isObject() && msg_topic.isMember("sdklog_level")) {
                                sdklog_level = msg_topic.get("sdklog_level", 3).asUInt();
                                pctrl_cfg->_general_map["sdklog_level"] = true;
                            }
                            if (msg_topic.isObject() && msg_topic.isMember("sdklog_autodel")) {
                                sdklog_autodel = msg_topic.get("sdklog_autodel", true).asBool();
                                pctrl_cfg->_general_map["sdklog_autodel"] = true;
                            }
                            if (msg_topic.isObject() && msg_topic.isMember("applog_level")) {
                                applog_level = msg_topic.get("applog_level", 1).asUInt();
                                pctrl_cfg->_general_map["applog_level"] = true;
                            }
                            if (msg_topic.isObject() && msg_topic.isMember("on_timer_delete_file")) {
                                on_timer_delete_file = msg_topic.get("on_timer_delete_file", true).asBool();
                                pctrl_cfg->_general_map["on_timer_delete_file"] = true;
                            }
                            if (msg_topic.isObject() && msg_topic.isMember("file_cache_interval")) {
                                file_cache_interval = msg_topic.get("file_cache_interval", 3600).asUInt();
                                pctrl_cfg->_general_map["file_cache_interval"] = true;
                            }
                            
                            pctrl_cfg->update_general_config(sdklog_dir,
                                                             sdklog_level,
                                                             sdklog_autodel,
                                                             applog_level,
                                                             on_timer_delete_file,
                                                             file_cache_interval);
                        }
                        if (cmd.compare("devinfo") == 0) {
                            std::string serial;
                            std::string ip;
                            uint32_t port;
                            std::string username;
                            std::string password;
                            
                            if (msg_topic.isObject() && msg_topic.isMember("serial")) {
                                serial = msg_topic.get("serial", "DS-9016HF-ST1620110524BBRR403574653WCVU").asString();
                                pctrl_cfg->_devinfo_map["serial"] = true;
                            }
                            if (msg_topic.isObject() && msg_topic.isMember("ip")) {
                                ip = msg_topic.get("ip", "127.0.0.1").asString();
                                pctrl_cfg->_devinfo_map["ip"] = true;
                            }
                            if (msg_topic.isObject() && msg_topic.isMember("port")) {
                                port = msg_topic.get("port", 8000).asUInt();
                                pctrl_cfg->_devinfo_map["port"] = true;
                            } 
                            if (msg_topic.isObject() && msg_topic.isMember("username")) {
                                username = msg_topic.get("username", "admin").asString();
                                pctrl_cfg->_devinfo_map["username"] = true;
                            }  
                            if (msg_topic.isObject() && msg_topic.isMember("password")) {
                                password = msg_topic.get("password", "12345").asString();
                                pctrl_cfg->_devinfo_map["password"] = true;
                            }                                                                                   
                            pctrl_cfg->update_devinfo_config(serial, ip, port, username, password);
                        }
                        if (cmd.compare("snapshot") == 0) {
                            std::string picture_path;
                            uint32_t picture_quality;
                            uint32_t picture_size;
                            std::string upload_mode;
                            std::string upload_path;
                            if (msg_topic.isObject() && msg_topic.isMember("picture_path")) {
                                picture_path = msg_topic.get("picture_path", "./imgs").asString();
                                pctrl_cfg->_snapshot_map["picture_path"] = true;
                            }
                            if (msg_topic.isObject() && msg_topic.isMember("picture_quality")) {
                                picture_quality = msg_topic.get("picture_quality", "").asUInt();
                                pctrl_cfg->_snapshot_map["picture_quality"] = true;
                            }
                            if (msg_topic.isObject() && msg_topic.isMember("picture_size")) {
                                picture_size = msg_topic.get("picture_size", "").asUInt();
                                pctrl_cfg->_snapshot_map["picture_size"] = true;
                            }
                            if (msg_topic.isObject() && msg_topic.isMember("upload_mode")) {
                                upload_mode = msg_topic.get("upload_mode", "").asString();
                                pctrl_cfg->_snapshot_map["upload_mode"] = true;
                            }
                            if (msg_topic.isObject() && msg_topic.isMember("upload_path")) {
                                upload_path = msg_topic.get("upload_path", "").asString();
                                pctrl_cfg->_snapshot_map["upload_path"] = true;
                            }                                                                                    
                            pctrl_cfg->update_snapshot_config(picture_path, 
                                                              picture_quality,
                                                              picture_size,
                                                              upload_mode, 
                                                              upload_path);
                        }
                        if (cmd.compare("mqtt") == 0) {
                            std::string mqtt_host;
                            uint32_t mqtt_port;
                            uint32_t mqtt_keepalive;
                            if (msg_topic.isObject() && msg_topic.isMember("host")) {
                                mqtt_host = msg_topic.get("host", "127.0.0.1").asString();
                                pctrl_cfg->_mqtt_map["host"] = true;
                            }
                            if (msg_topic.isObject() && msg_topic.isMember("port")) {
                                mqtt_port = msg_topic.get("port", 1883).asUInt();
                                pctrl_cfg->_mqtt_map["port"] = true;
                            }
                            if (msg_topic.isObject() && msg_topic.isMember("keepalive")) {
                                mqtt_keepalive = msg_topic.get("keepalive", 60).asUInt();
                                pctrl_cfg->_mqtt_map["keepalive"] = true;
                            }
                            pctrl_cfg->update_mqtt_config(mqtt_host, mqtt_port, mqtt_keepalive);
                        }
                    }
                } else if (strstr((char*)msg->topic, "/mpack") != NULL) {

                }
            }
        }
    }
}

bool CtrlMqtt::mqttctrl_jpgsnap_get_stream(uint32_t channel, char *jpgbuf, uint32_t jpglen, uint32_t num) {
    //SpdLog::console->info("snapshot picture stream to mpack start");

    char* mpdata = NULL;
    size_t mpsize = 0;

    mpack_writer_t mpwriter;
    mpack_writer_init_growable(&mpwriter, &mpdata, &mpsize);
    mpack_start_map(&mpwriter, 4);
    mpack_write_cstr(&mpwriter, "api");
    mpack_write_cstr(&mpwriter, "v1.0");
    mpack_write_cstr(&mpwriter, "cmd");
    mpack_write_cstr(&mpwriter, "jpeg-mpack");
    mpack_write_cstr(&mpwriter, "value");
    mpack_start_map(&mpwriter, 2);
    mpack_write_cstr(&mpwriter, "channel");
    mpack_write_u32(&mpwriter, channel);
    //mpack_write_cstr(&mpwriter, "project-id");
    //mpack_write_cstr(&mpwriter, "xxxxx");
    //mpack_write_cstr(&mpwriter, "tag-id");
    //mpack_write_cstr(&mpwriter, "xxxxx");
    //mpack_write_cstr(&mpwriter, "check");
    //mpack_write_cstr(&mpwriter, "Q1d2e4U7");
    //mpack_write_cstr(&mpwriter, "num");
    //mpack_write_u32(&mpwriter, 0);
    mpack_write_cstr(&mpwriter, "data");
    mpack_write_bin(&mpwriter, jpgbuf, jpglen);
    //mpack_write_cstr(&mpwriter, "md5");
    //mpack_write_cstr(&mpwriter, md5_hash(jpgbuf,jpglen));
    mpack_finish_map(&mpwriter);
    mpack_write_cstr(&mpwriter, "date");
    mpack_write_u32(&mpwriter, time(NULL));
    mpack_finish_map(&mpwriter);
    //IPCAM_DEBUG("JPGSNAP_SBUF %d",mpsize);
    /*if (mpack_writer_destroy(&mpwriter) != mpack_ok) {
        CHAR msg[128]={0};
        sprintf(msg,"snap encode at %s buffer 0x%08X size %d",(strlen(lpinfo->snap_check)>0)?lpinfo->snap_check:"(none)",(int)jpgbuf,jpglen);
        mqttctrl_status_json(obj,"error",msg,0xFFFFFE00);
    }*/
    //mqttctrl_public_buf(lpinfo, MQTT_PUB_CLIENT_SBUF, mpdata, mpsize, lpinfo->snap_qos,(lpinfo->snap_msave==true));
    
    if (mpack_writer_destroy(&mpwriter) != mpack_ok) {

    }
    
    int rc = publish(NULL, mqtt_pub[MQTT_PUB_CLIENT_SBUF], mpsize, mpdata);
    free(mpdata);

    return true;
}

bool CtrlMqtt::upload_jpgsnap_by_mpack() {
    std::string file_path;
    file_path = pctrl_cfg->get_config_st().snapshot_picture_path;
                            
    DIR *dirp;
    struct dirent *direntp;
    dirp = opendir(file_path.c_str());
    if(dirp != NULL) {
        while(1) {
            direntp = readdir(dirp);
            if(direntp == NULL)
                break;
            else if(direntp->d_name[0] != '.') {
                std::string file_name = direntp->d_name;
                int file_size = strlen(file_name.c_str());
               
                if (strcmp((file_name.c_str() + ( file_size - 5)), ".jpeg") != 0) {
                    continue;
                }

                char channel_c = file_name.at(3);
                std::string channel_str(1, channel_c);
                uint32_t channel = std::stoi(channel_str);

                std::string picture_file;
                if (file_path.back() != '/')
                    picture_file = file_path + "/" + file_name;
                else
                    picture_file = file_path + file_name;
                
                char * buffer;
                long size;
                std::ifstream image;
                image.open (picture_file, std::ifstream::in);
                if (image.good()) {
                    image.seekg(0, std::ifstream::end);
                    size = image.tellg();
                    image.seekg(0);
                    buffer = new char [size];
                    
                    mqttctrl_jpgsnap_get_stream(channel, buffer, size, 0);
                }
                if (buffer)
                    delete buffer;
                image.close();
                
            }
        }
        closedir(dirp);
    }
    
    return true;
}

void CtrlMqtt::save_topics() {
    CtrlCfg::Yaml_Config_ST config_st = pctrl_cfg->get_config_st();
    char *host = config_st.mqtt_host;
    uint32_t port = config_st.mqtt_port;
    uint32_t keepalive = config_st.mqtt_keepalive;

    connect(host, port, keepalive);

    /*snprintf(mqtt_sub[MQTT_SUB_REPORT], MQSIZE,"%s/%s", TopicRoot, "report");
    subscribe(NULL, mqtt_sub[MQTT_SUB_REPORT], 0);
    SpdLog::console->info("mqtt subcribe topic: {0}", mqtt_sub[MQTT_SUB_REPORT]);

    //publish topics
    snprintf(mqtt_pub[MQTT_PUB_CLIENT_CTRL], MQSIZE, "%s/%s", TopicRoot, "ctrlinfo");
    mhikv_publish_topics.push_front(mqtt_pub[MQTT_PUB_CLIENT_CTRL]);
    
    snprintf(mqtt_pub[MQTT_PUB_CLIENT_SBUF], MQSIZE, "%s/%s", TopicRoot, "mpack");
    mhikv_publish_topics.push_front(mqtt_pub[MQTT_PUB_CLIENT_SBUF]);

    //subscribe topics
    snprintf(mqtt_sub[MQTT_SUB_CLIENT_CTRL], MQSIZE, "%s/%s", TopicRoot, "ctrlinfo");
    mhikv_subscribe_topics.push_front(mqtt_sub[MQTT_SUB_CLIENT_CTRL]);
    subscribe(NULL, mqtt_sub[MQTT_SUB_CLIENT_CTRL], 0);
    SpdLog::console->info("mqtt subcribe topic: {0}", mqtt_sub[MQTT_SUB_CLIENT_CTRL]);
    
    snprintf(mqtt_sub[MQTT_SUB_CLIENT_SBUF], MQSIZE, "%s/%s", TopicRoot, "mpack");
    mhikv_subscribe_topics.push_front(mqtt_sub[MQTT_SUB_CLIENT_SBUF]);
    subscribe(NULL, mqtt_sub[MQTT_SUB_CLIENT_SBUF], 0);
    SpdLog::console->info("mqtt subcribe topic: {0}", mqtt_sub[MQTT_SUB_CLIENT_SBUF]);*/
}

int CtrlMqtt::start_mqtt_cycle() {
    uint64_t start_time = sand::now();
    
    while (1) {
        int rc;
        while (true) {
            sand::sleep(100);
            if (mqtt_run) {
                rc = loop();
                SpdLog::console->info("mqtt -> loop rc: {0}, error: {1}", rc,  mosquitto_strerror(rc));
                if (rc != MOSQ_ERR_SUCCESS) {
                    sand::sleep(100);
                                    
                    SpdLog::console->error("mqtt -> reconnect(), rc: {0}, error: {1}", rc,  mosquitto_strerror(rc));
                } 

                CtrlCfg::Yaml_Config_ST config_st = pctrl_cfg->get_config_st();
                bool is_open_timer_delete = config_st.on_timer_delete_file;
                if (is_open_timer_delete) {
                    sand::sleep(100);
                    uint64_t current_time = sand::now();
                    uint64_t diff_time = current_time - start_time;
                    uint64_t seconds = pctrl_cfg->get_config_st().file_cache_interval;

                    if (diff_time >= seconds*1000) {
                        pctrl_app->timer_delete_cache_file();
                        start_time = sand::now();
                    }
                }
            }
            if (!mqtt_run) {
                break;
            }
        }
        
    }
}

void CtrlMqtt::add_topics(std::string method, std::string topic, CtrlMqtt::TOPIC_T type) {
    if (type == TOPIC_SUBSCRIBE) {

    } else {

    }
}

void CtrlMqtt::clear_topics(CtrlMqtt::TOPIC_T type) {
    if (type == TOPIC_SUBSCRIBE) {

    } else {

    }
}

bool CtrlMqtt::create_picdir(int len, const char *jpgbufer) {

    return true;
}
